/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2018-2021.
 * Description: support cetartlb feature
 * Author: fanglinxu <fanglinxu@huawei.com>
 * Create: 2018-09-13
 */

#ifndef _LINUX_CETARTLB_H
#define _LINUX_CETARTLB_H

#include <linux/module.h>
#include <asm/pgalloc.h>
#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
#include <linux/pgtable.h>
#endif
#include <linux/mmu_notifier.h>
#include <linux/mm.h>

#ifdef CONFIG_X86_64
#define CETARTLB_SHIFT_1G  PUD_SHIFT
#define CETARTLB_PAGESIZE_1G   PUD_PAGE_SIZE
#define CETARTLB_PAGE_MASK_1G  PUD_PAGE_MASK

#define CETARTLB_SHIFT_2M  PMD_SHIFT
#define CETARTLB_PAGESIZE_2M   PMD_PAGE_SIZE
#define CETARTLB_PAGE_MASK_2M  PMD_PAGE_MASK
#endif /* CONFIG_X86_64 */

#ifdef CONFIG_ARM
#ifdef CONFIG_ARM_LPAE
#define CETARTLB_SHIFT_1G  PGDIR_SHIFT
#define CETARTLB_PAGESIZE_1G   PGDIR_SIZE
#define CETARTLB_PAGE_MASK_1G  PGDIR_MASK

#define CETARTLB_SHIFT_2M  PMD_SHIFT
#define CETARTLB_PAGESIZE_2M   PMD_SIZE
#define CETARTLB_PAGE_MASK_2M  PMD_MASK
#else
#define CETARTLB_SHIFT_1G  PGDIR_SHIFT
#define CETARTLB_PAGESIZE_1G   PGDIR_SIZE
#define CETARTLB_PAGE_MASK_1G  PGDIR_MASK

#define CETARTLB_SHIFT_16M  SUPERSECTION_SHIFT
#define CETARTLB_PAGESIZE_16M   SUPERSECTION_SIZE
#define CETARTLB_PAGE_MASK_16M  SUPERSECTION_MASK

#define CETARTLB_SHIFT_2M  PMD_SHIFT
#define CETARTLB_PAGESIZE_2M   PMD_SIZE
#define CETARTLB_PAGE_MASK_2M  PMD_MASK
#endif /* CONFIG_ARM_LPAE */
#endif /* CONFIG_ARM */

#ifdef CONFIG_ARM64
#if CONFIG_PGTABLE_LEVELS > 3
#define CETARTLB_SHIFT_1G  PUD_SHIFT
#define CETARTLB_PAGESIZE_1G   PUD_SIZE
#define CETARTLB_PAGE_MASK_1G  PUD_MASK
#else
#define CETARTLB_SHIFT_1G  PGDIR_SHIFT
#define CETARTLB_PAGESIZE_1G   PGDIR_SIZE
#define CETARTLB_PAGE_MASK_1G  PGDIR_MASK
#endif /* CONFIG_PGTABLE_LEVELS */
#define CETARTLB_SHIFT_2M  PMD_SHIFT
#define CETARTLB_PAGESIZE_2M   PMD_SIZE
#define CETARTLB_PAGE_MASK_2M  PMD_MASK
#endif /* CONFIG_ARM64 */

int is_file_cetartlb_2m(struct file *file);
int is_file_cetartlb_1g(struct file *file);

static inline unsigned long cetatlb_flags_to_size(unsigned long rtos_vm_flags)
{
	if (rtos_vm_flags & RTOS_VM_CETARTLB_2M)
		return CETARTLB_PAGESIZE_2M;

#if defined(CONFIG_ARM) && !defined(CONFIG_ARM_LPAE)
	if (rtos_vm_flags & RTOS_VM_CETARTLB_16M)
		return CETARTLB_PAGESIZE_16M;
#else
	if (rtos_vm_flags & RTOS_VM_CETARTLB_1G)
		return CETARTLB_PAGESIZE_1G;
#endif

	return PAGE_SIZE;
}

static inline unsigned long cetartlb_size_to_flags(unsigned long cetartlb_size)
{
	if (cetartlb_size == CETARTLB_PAGESIZE_2M)
		return RTOS_VM_CETARTLB_2M;

#if defined(CONFIG_ARM) && !defined(CONFIG_ARM_LPAE)
	if (cetartlb_size == CETARTLB_PAGESIZE_16M)
		return RTOS_VM_CETARTLB_16M;
#else
	if (cetartlb_size == CETARTLB_PAGESIZE_1G)
		return RTOS_VM_CETARTLB_1G;
#endif

	return 0;
}

static inline int is_vm_cetatlb_page(const struct vm_area_struct *vma)
{
#if defined(CONFIG_ARM) && !defined(CONFIG_ARM_LPAE)
	if (vma->rtos_vm_flags & RTOS_VM_CETARTLB_16M)
		return 1;
#endif

	if ((vma->rtos_vm_flags & RTOS_VM_CETARTLB_1G)
			|| (vma->rtos_vm_flags & RTOS_VM_CETARTLB_2M))
		return 1;

	return 0;
}

void unmap_cetatlbpage_range(struct vm_area_struct *,
		unsigned long, unsigned long, struct page *);

int remap_cetatlb_range(struct vm_area_struct *vma, unsigned long addr,
		unsigned long pfn, unsigned long size, struct file *file);

unsigned long cetartlb_change_protection(struct vm_area_struct *vma,
		unsigned long address, unsigned long end, pgprot_t newprot);

extern int ceta_mmap_mem_2M(struct file *file, struct vm_area_struct *vma);
extern unsigned long get_unmapped_area_mem_2M(struct file *file, unsigned long addr,
		unsigned long len, unsigned long pgoff, unsigned long flags);

#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
extern unsigned long long cetartlb_addr;
extern unsigned long long cetartlb_size;
#endif
#ifdef CONFIG_X86_64
/* depend on CONFIG_ARCH_WANT_HUGE_PMD_SHARE, auto select by x86_64 */
extern bool rtlb_vma_shareable(struct vm_area_struct *vma, unsigned long addr);
extern unsigned long rtlb_page_table_shareable(struct vm_area_struct *svma,
				struct vm_area_struct *vma,
				unsigned long addr, pgoff_t idx);
#endif

static inline int ceta_is_special(void *orig)
{
	pte_t pte = *(pte_t *)orig;

	if (pte_special(pte))
		return 1;
	return 0;
}

#endif
